package com.blue.excel.util;

import com.blue.excel.annotation.ColField;
import com.blue.excel.annotation.SheetInfo;
import com.blue.excel.model.CellStyleInfo;
import com.blue.excel.model.ColumnField;
import com.blue.excel.model.OverrideTitle;
import com.blue.excel.type.CellType;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;

import java.lang.reflect.Field;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Created by ChenYaHui on 2017/8/2.
 */
public abstract class ExcelTransformerBase {

    protected Workbook workbook;

    protected Class currentEntityClass;

    protected Sheet currentSheet;

    protected SheetInfo currentSheetInfo;

    protected List<ColumnField> columnFields = new ArrayList<>();

    protected ExcelHelper excelHelper;

    protected Row stackRow;

    protected Row titleRow;

    protected Row remarkRow;

    protected CellStyle stackStyle;
    protected CellStyle titleStyle;
    protected CellStyle remarkStyle;

    protected boolean hasStackRow;
    protected boolean hasRemarkRow;
    protected boolean colorSeparated = true;

    protected boolean hasBigTitle;
    protected String bigTitle;

    protected int currentRowIndex;

    protected Map<Integer,Integer> columnsWidth = new HashMap<>();

    protected Field bgColorField;

    public ExcelTransformerBase() {
        workbook = new HSSFWorkbook();

        stackStyle = workbook.createCellStyle();
        stackStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        stackStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);

        titleStyle = workbook.createCellStyle();
        titleStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        titleStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);

        remarkStyle = workbook.createCellStyle();
        remarkStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        remarkStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);

        excelHelper = new ExcelHelper(workbook);
    }

//    public ExcelTransformerBase(String bigTitle) {
//        this();
//        this.bigTitle = bigTitle;
//        hasBigTitle = true;
//    }

    public void setColorSeparate(boolean separate){
        colorSeparated = separate;
    }


    public Workbook getWorkbook(){
        return workbook;
    }

    public void makeSheet(Class clasz){
        makeSheet(clasz,null, null, null);
    }

    public void makeSheet(Class clasz,String sheetName){
        makeSheet(clasz, sheetName, null, null);
    }

    public void makeSheet(Class clasz,String sheetName, List<OverrideTitle> overrideTitles){
        makeSheet(clasz,sheetName, overrideTitles, null);
    }


    public void makeSheet(Class clasz,String sheetName, List<OverrideTitle> overrideTitles, List<String> resortFieldNames){
        currentEntityClass = clasz;
        if(clasz.isAnnotationPresent(SheetInfo.class)){
            currentSheetInfo = (SheetInfo) clasz.getAnnotation(SheetInfo.class);
        }

        if(sheetName != null)
            currentSheet = workbook.createSheet(sheetName);
        else{
            if(currentSheetInfo != null && !"".equals(currentSheetInfo.name())){
                currentSheet = workbook.createSheet(currentSheetInfo.name());
            }else{
                currentSheet = workbook.createSheet();
            }
        }

        excelHelper.setCurrentSheet(currentSheet);


        prepareSheet(clasz,overrideTitles,resortFieldNames);
    }

    public void setNextSheetBigTitle(String title){
        bigTitle = title;
//        Row firstRow = excelHelper.getOrCreateRow(0);
//        Cell firstCell = excelHelper.getOrCreateCell(firstRow,0);
//        firstCell.setCellValue(title);
    }

    public <E> void parseToSheet(Collection<E> collection,Class<E> clasz){
        if(clasz != currentEntityClass){
            return ;
        }
        Set<Integer> deeperBgColorRegions = new HashSet<>();
        Set<Integer> notMergeCols = new HashSet<>();
        Iterator<E> iterator = collection.iterator();
        List<Integer> insertRowIndexs = new ArrayList<>();
        int insertRowIndex = currentRowIndex;
        insertRowIndexs.add(insertRowIndex);
        while (iterator.hasNext()){
            E item = iterator.next();
            try {
                insertRowIndex = entityToRow(item,deeperBgColorRegions,notMergeCols);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            insertRowIndexs.add(insertRowIndex);
        }

        int maxDataCols = getMaxDataCols();
        for(int j = 0; j < insertRowIndexs.size() - 1; j++){
            for(int i = 0; i < maxDataCols; i++){
                if(!notMergeCols.contains(i)){
                    excelHelper.mergeRegion(insertRowIndexs.get(j),insertRowIndexs.get(j + 1) - 1,i,i);
                }
            }
        }


        if(hasBigTitle){
            int bigTitleMergeRegionIndex = excelHelper.getMergeRegion(0,0);
            currentSheet.removeMergedRegion(bigTitleMergeRegionIndex);
            Row bigTitleRow = excelHelper.getOrCreateRow(0);
            Cell bigTitleCell = excelHelper.getOrCreateCell(bigTitleRow, 0);
//            String bigTitle = bigTitleCell.getStringCellValue();
            bigTitleCell.setCellValue(bigTitle);
            excelHelper.mergeRegion(0,0,0,maxDataCols - 1);
        }


        if (colorSeparated) {
            if(bgColorField != null){
                for(int i = insertRowIndexs.get(0); i < insertRowIndexs.get(insertRowIndexs.size() - 1); i++){
                    Row dataRow = excelHelper.getOrCreateRow(i);
                    for(int j = 0; j < maxDataCols; j++){
                        Cell dataCell = excelHelper.getOrCreateCell(dataRow,j);
                        if(deeperBgColorRegions.contains(i)){
                            excelHelper.setBgColorGray(dataCell);
                        }
                        else{
                            excelHelper.setBgColorWhite(dataCell);
                        }
                    }
                }
            }else{
                for(int j = 0; j < insertRowIndexs.size() - 1; j++){
                    int nextInsertRowIndex = insertRowIndexs.get(j + 1);
                    for(int outter = insertRowIndexs.get(j); outter < nextInsertRowIndex; outter ++){
                        Row dataRow = excelHelper.getOrCreateRow(outter);
                        for(int inner = 0; inner < maxDataCols; inner ++){
                            Cell dataCell = excelHelper.getOrCreateCell(dataRow,inner);
                            if(j % 2 == 0){
                                excelHelper.setBgColorGray(dataCell);
                            }
                            else{
                                excelHelper.setBgColorWhite(dataCell);
                            }
                        }
                    }
                }
            }

        }


        makeFirstDataRowBorder();
        for(Integer index : columnsWidth.keySet()){
            currentSheet.setColumnWidth(index,columnsWidth.get(index));
        }
    }


    protected void fillChildrenColumnFields(ColumnField columnField, Class clasz){
        Field[] innerFields = clasz.getFields();
        for(Field innerField : innerFields){
            if(innerField.isAnnotationPresent(ColField.class)){
                ColField colField = innerField.getAnnotation(ColField.class);
                columnField.columnFields.add(new ColumnField(innerField,colField));
            }
        }
    }

    protected Cell setTitleCellValue(int index,String value){
        Cell cell = excelHelper.getOrCreateCell(titleRow,index);
        cell.setCellType(Cell.CELL_TYPE_STRING);
        cell.setCellStyle(titleStyle);
        cell.setCellValue(value);
        return cell;
    }


    protected int getMaxDataCols(){
        int maxNum = 0;
        for(int i = 0; i < columnFields.size(); i++){
            maxNum += columnFields.get(i).getSpan();
        }
        return maxNum;
    }

    protected void dealColumnWidth(Cell cell){
        int contentLength = excelHelper.getContentLength(cell);
        int columnIndex = cell.getColumnIndex();
        Integer thisColMaxLenght = columnsWidth.get(columnIndex);
        if(thisColMaxLenght == null || contentLength > thisColMaxLenght){
            columnsWidth.put(columnIndex,contentLength);
        }

    }

    protected void dealColumnWidth(Cell cell, int columnIndex){
        int contentLength = excelHelper.getContentLength(cell);
        Integer thisColMaxLenght = columnsWidth.get(columnIndex);
        if(thisColMaxLenght == null || contentLength > thisColMaxLenght){
            columnsWidth.put(columnIndex,contentLength);
        }
    }



    protected int getNextColumnIndex(int currentColumnFieldIndex){
        int result = 0;
        for(int i = 0; i < currentColumnFieldIndex; i++){
            ColumnField columnField = columnFields.get(i);
            result += columnField.getSpan();
        }
        return result;
    }

    private void makeFirstDataRowBorder(){
        int firstRowIndex = titleRow.getRowNum() + 1;
        if(hasRemarkRow){
            firstRowIndex ++;
        }

        Row firstDataRow = currentSheet.getRow(firstRowIndex);
        int firstDataRowMax = getNextColumnIndex(columnFields.size()) - 1;
        for(int i = 0; i < (firstDataRowMax + 1); i++){
            Cell firstRowCell = excelHelper.getOrCreateCell(firstDataRow, i);
            CellStyleInfo cellStyleInfo = new CellStyleInfo(firstRowCell.getCellStyle());
            cellStyleInfo.borderTop = HSSFCellStyle.BORDER_MEDIUM;
            firstRowCell.setCellStyle(excelHelper.getCellStyle(cellStyleInfo));
        }
    }


    public void addSetIndex(Set<Integer> set,int nowIndex,int step){
        Set<Integer> newNotMergeCols = new HashSet<>();
        for(Integer index : set){
            if(index >= nowIndex){
                newNotMergeCols.add(index + step);
            }
            else{
                newNotMergeCols.add(index);
            }
        }
        set.clear();
        set.addAll(newNotMergeCols);
    }

    public Map<Integer,Integer> addMapIndex(Map<Integer,Integer> map,int nowIndex,int step){
        Map<Integer,Integer> newMap = new HashMap<>();
        for(Integer index : map.keySet()){
            if(index >= nowIndex){
                newMap.put(index + step, map.get(index));
            }else{
                newMap.put(index, map.get(index));
            }
        }
        return newMap;
    }


    public abstract void prepareSheet(Class clasz,List<OverrideTitle> overrideTitles, List<String> resortFieldNames);


    public abstract int entityToRow(Object entity,Set<Integer> deeperBgColorRegions,Set<Integer> notMergeCols) throws IllegalAccessException;
}
